package net.pimpas.web.jsf.renderers.simplemenu.html;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlInputHidden;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ActionEvent;
import javax.servlet.http.HttpServletRequest;

import net.pimpas.logging.serviceprovider.LoggingServiceProvider;
import net.pimpas.logging.serviceprovider.LoggingServiceProviderFactoryImpl;
import net.pimpas.resourcebundle.exception.MessageKeyNotFoundException;
import net.pimpas.resourcebundle.exception.ResourceBundleNotFoundException;
import net.pimpas.resourcebundle.serviceprovider.ResourceBundleServiceProvider;
import net.pimpas.resourcebundle.serviceprovider.ResourceBundleServiceProviderFactoryImpl;
import net.pimpas.resourcebundle.serviceprovider.ResourceBundleServiceProviderType;
import net.pimpas.util.reflection.ReflectionUtil;
import net.pimpas.web.jsf.components.simplemenu.SimpleMenu;
import net.pimpas.web.jsf.components.simplemenu.SimpleMenuBar;
import net.pimpas.web.jsf.components.simplemenu.SimpleMenuButton;
import net.pimpas.web.jsf.components.simplemenu.SimpleMenuElement;
import net.pimpas.web.jsf.components.simplemenu.SimpleMenuItem;
import net.pimpas.web.jsf.components.simplemenu.SimpleMenuSeparator;
import net.pimpas.web.jsf.components.simplemenu.UISimpleMenu;
import net.pimpas.web.jsf.components.simplemenu.impl.SimpleMenuBarImpl;
import net.pimpas.web.jsf.components.simplemenu.impl.SimpleMenuButtonImpl;
import net.pimpas.web.jsf.components.simplemenu.impl.SimpleMenuItemImpl;
import net.pimpas.web.jsf.components.span.UISpan;
import net.pimpas.web.jsf.renderers.RendererBase;
import net.pimpas.web.jsf.util.ComponentUtils;

import org.apache.myfaces.shared_impl.renderkit.html.HTML;

public class HtmlSimpleMenuRenderer extends RendererBase {
	
	private static LoggingServiceProvider logger = LoggingServiceProviderFactoryImpl.
	getLoggingServiceProvider(HtmlSimpleMenuRenderer.class);
	private static ResourceBundleServiceProvider bundle = 
		ResourceBundleServiceProviderFactoryImpl.getResourceBundleServiceProvider();
	
	@Override
	public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
		if(!component.isRendered()) return;
		if(!ComponentUtils.isCorrectComponentType(component, UISimpleMenu.class)) return;
		
		UISimpleMenu menu = (UISimpleMenu) component;
		// render span
		this.appendSpan(context, menu);
		// render input hidden
		this.appendHidden( context, menu );
		
		super.initResourceManager(context, component);
	}

	@SuppressWarnings("unchecked")
	private void appendHidden(FacesContext context, UISimpleMenu menu) {
		if(ComponentUtils.getAllComponentsByClass(menu, HtmlInputHidden.class).size() > 0)
			return;
		
		HtmlInputHidden hidden = (HtmlInputHidden) context.getApplication().createComponent(HtmlInputHidden.COMPONENT_TYPE);
		hidden.setId(menu.getId()+UISimpleMenu.DEFAULT_SUFIX_HIDDEN);
		
		menu.getChildren().add(hidden);
	}

	@SuppressWarnings("unchecked")
	private void appendSpan(FacesContext context, UISimpleMenu menu) {
		if(ComponentUtils.getAllComponentsByClass(menu, UISpan.class).size() > 0)
			return;
		
		UISpan sp = (UISpan) context.getApplication().createComponent(UISpan.COMPONENT_TYPE);
		sp.setNoId(Boolean.FALSE);
		sp.setId(menu.getId()+UISimpleMenu.DEFAULT_SUFX_CONTAINER);
		menu.getChildren().add(sp);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
		if(!component.isRendered()) return;
		if(!ComponentUtils.isCorrectComponentType(component, UISimpleMenu.class)) return;
		
		UISimpleMenu menu = (UISimpleMenu) component;
		// get value
		List value = (List) menu.getValueBinding("value").getValue(context);
		if(value == null) {
			try {
				logger.debug(bundle.getMessage(this.getClass(), "emptyMenus", ResourceBundleServiceProviderType.LOG, menu.getId()));
			} catch (ResourceBundleNotFoundException e) {
			} catch (MessageKeyNotFoundException e) {
			}
			
			return;
		}
		
		this.processImports( context, menu );
		
		ResponseWriter writer = context.getResponseWriter();
		writer.startElement(HTML.SCRIPT_ELEM, menu);
		
		// write menubar
		SimpleMenuBar bar = new SimpleMenuBarImpl(menu.getId()+UISimpleMenu.DEFAULT_SUFIX_MENU_BAR);
		SimpleMenuElement element = null;
		for (Iterator<SimpleMenuElement> iter = value.iterator(); iter.hasNext();) {
			element = iter.next();
			bar.addChild(new SimpleMenuButtonImpl(element.getName()+UISimpleMenu.DEFAULT_SUFIX_BUTTON, element));
		}
		
		// span innerhtml
		this.processMenuBarAndElements( context, writer, menu, bar, value );

		UIComponent cp = ComponentUtils.getAllComponentsByClass(component, UISpan.class).get(0);
		writer.write("simpleMenuImagePath = '"+this.getResourceManager().getResourceUri("images/")+"';");
		writer.write("document.getElementById('"+cp.getClientId(context)+"').innerHTML = "+bar.getName()+".toString( );");
		writer.endElement(HTML.SCRIPT_ELEM);
	}

	private void processImports(FacesContext context, UISimpleMenu menu) {
		if(menu.getStyleClass() != null && !"".equals(menu.getStyleClass()))
			// ignore theme
			this.getResourceManager().addStylesheetHere(menu.getStyleClass());
		else {
			// use theme
			int pos = this.getThemeStylePosition(menu.getTheme());
			if(pos == -1) {
				// TODO exception
				return;
			}
			
			String themeFile = UISimpleMenu.DEFAULT_STYLE_FILES[pos];
			this.getResourceManager().addStylesheetHeader("css/"+themeFile);
		}
		
		// javascript
		this.getResourceManager().addJavascriptHeader("js/"+UISimpleMenu.DEFAULT_JS_FILE);
	}

	private void processMenuBarAndElements(FacesContext context, ResponseWriter writer, UISimpleMenu menu, SimpleMenuBar bar, List value)
	 	throws IOException {
		StringBuffer buff = new StringBuffer();
		UIComponent cp = ComponentUtils.getAllComponentsByClass(menu, HtmlInputHidden.class).get(0);
		
		buff.append("var "+bar.getName()+" = new simpleMenuBar('"+cp.getClientId(context)+"');");
		for(int i=0;i<bar.getChildren().size();i++) {
			// render buttons
			buff.append(this.renderButton((SimpleMenuButton)bar.getChildren().get(i), context, menu, writer, (SimpleMenuElement) value.get(i)));
			buff.append(bar.getName()+".add("+((SimpleMenuButton)bar.getChildren().get(i)).getName()+");");
		}
		
		writer.write(new String(buff));
	}
	
	private String renderElement( SimpleMenuElement element ) {
		if(ReflectionUtil.isSubclass(element.getClass(), SimpleMenu.class))
			return this.renderMenu((SimpleMenu) element);
		else if(ReflectionUtil.isSubclass(element.getClass(), SimpleMenuSeparator.class))
			return this.renderSeparator((SimpleMenuSeparator) element);
		return this.renderMenuItem((SimpleMenuItem) element);
	}
	
	private String renderSeparator(SimpleMenuSeparator separator) {
		return "new simpleMenuSeparator()";
	}
	
	private String renderMenu( SimpleMenu menu ) {
		StringBuffer buff = new StringBuffer();
		buff.append("var "+menu.getName()+ " = new simpleMenu;");
		SimpleMenuItem smi = null;
		for(int i=0;i<menu.getChildren().size();i++) {
			if(ReflectionUtil.isSubclass(menu.getChildren().get(i).getClass(), SimpleMenuSeparator.class))
				buff.append(menu.getName()+".add("+this.renderElement(menu.getChildren().get(i))+");");
			else {
				buff.append(this.renderElement(menu.getChildren().get(i)));
				if(ReflectionUtil.isSubclass(menu.getChildren().get(i).getClass(), SimpleMenuItem.class))
					buff.append(menu.getName()+".add("+menu.getChildren().get(i).getName()+");");
				else if(ReflectionUtil.isSubclass(menu.getChildren().get(i).getClass(), SimpleMenu.class)) {
					smi = new SimpleMenuItemImpl(menu.getChildren().get(i).getName()+UISimpleMenu.DEFAULT_SUFIX_ITEM_ACCESS);
					smi.setLabel(menu.getChildren().get(i).getLabel());
					smi.setTooltip(menu.getChildren().get(i).getTooltip());
					smi.setIcon(menu.getChildren().get(i).getIcon());
					smi.setIconHover(menu.getChildren().get(i).getIconHover());
					smi.setParent(menu.getChildren().get(i));
					buff.append(this.renderElement(smi));
					buff.append(menu.getName()+".add("+smi.getName()+");");
				}
			}
		}
		
		return new String(buff);
		
	}
	
	private String renderMenuItem( SimpleMenuItem item ) {
		return "var "+item.getName()+" = new simpleMenuItemIcon("+((item.getIcon() != null) ? "'"+item.getIcon()+"'" : "null")+"," +
		((item.getIconHover() != null) ? "'"+item.getIconHover()+"'" : "null")+"," +
				"'"+item.getLabel()+"', "+((item.getAction()==null) ? "null" : "'"+item.getAction()+"'")+", " +
		((item.getTooltip() != null) ? "'"+item.getTooltip()+"'" : "null")+
		((item.getParent() != null) ? ","+item.getParent().getName() : "") + ");";
	}
	
	private String renderButton(SimpleMenuButton button, FacesContext context, UISimpleMenu menu, ResponseWriter writer, SimpleMenuElement menuElement) throws IOException {
		StringBuffer buff = new StringBuffer();
		buff.append(this.renderElement(menuElement));
		buff.append("var "+button.getName()+" = new simpleMenuButton(");
		buff.append("'"+button.getLabel()+"',"+((button.getAction()!=null) ? "'"+button.getAction()+"'" : "null")+",");
		buff.append(((button.getTooltip() != null) ? "'"+button.getTooltip()+"'" : "null"));
		buff.append(((ReflectionUtil.isSubclass(menuElement.getClass(), SimpleMenu.class)) ? ", "+menuElement.getName() : ""));
		buff.append(");");
		return new String(buff);
	}

	private int getThemeStylePosition(String theme) {
		for(int i=0;i<UISimpleMenu.THEMES.length;i++)
			if(UISimpleMenu.THEMES[i].equals(theme)) return i;
		return -1;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void decode(FacesContext context, UIComponent component) {
		HttpServletRequest request  = (HttpServletRequest) context.getExternalContext() .getRequest();
		// get span
		UIComponent cp = ComponentUtils.getAllComponentsByClass(component, HtmlInputHidden.class).get(0);
		String parameterValue = request.getParameter(cp.getClientId(context));
		if(parameterValue != null && !"".equals(parameterValue)) {
			component.getAttributes().put(UISimpleMenu.MENU_ITEM_CLICKED, parameterValue);
			component.queueEvent(new ActionEvent(component));
		}
	}
}